package socket;

import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

public class Server2 {
    /*
        java.net.ServerSocket
        运行在服务端，主要作用有两个:
        1:向系统申请服务端口，客户端可以通过该端口与服务端建立链接
        2:监听服务端口，一旦一个客户端连接了，就会立即创建一个Socket实例与该客户端交互
          如果将Socket比喻为"电话",那么ServerSocket就相当于是客服中心的"总机"

          群聊步骤
          1.设计list集合allOut
          2.在run中获取pw后将其存入到allOut
          3.设计sendMessageToAll()方法群发消息
          4.run中调用方法
     */
    private ServerSocket serverSocket; //总机
    //设计集合存放所有客户端输出流,用于群发
    private List<PrintWriter> allout =new ArrayList<>();

    public Server2() {
        try {
            System.out.println("正启动服务器");
            /*
              创建ServerSocket对象时，会申请一个端口
              如果该端口被其他程序占用，则会抛出异常:
                java.net.BindException: Address already in use:
              解决办法:
              1.杀死占用该端口的进程(通常发生在服务端启动两次导致)
              2.更换端口号
             */
            serverSocket = new ServerSocket(9121);
            System.out.println("服务器启动完毕");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 该方法用于启动程序的执行
     */
    public void start() {
        try {
            while (true) {
                System.out.println("等待客户端连接...");
                /*
                 ServerSocket提供的重要方法:
                   Socket accept():用于接收客户端的连接，并返回一个Socket实例与连接的客户端
交互。
                  该方法是一个阻塞方法，调用后程序会"卡住"，直到一个客户端连接为止。
                */
                Socket socket = serverSocket.accept();
                System.out.println("一个客户端连接了!");

                //启动一个线程来处理客户端的交互
                ClientHandler handler=new ClientHandler(socket);
                Thread t=new Thread(handler);
                t.start();
            }
        } catch (IOException e) {
        }
    }

    public static void main(String[] args) {
        Server2 server2 = new Server2();
        server2.start();
    }

    /*
    该线程任务用于负责与指定的客户端交互
     */
    private class ClientHandler implements Runnable{
       private Socket socket;
       public ClientHandler(Socket socket){
           this.socket=socket;
       }
         public void run(){
             try {
                 //接收客户端发送过来的消息
                /*
                  Socket提供的方法:
                  InputStream getInputStream()
                  通过socket获取输入流可以读取客户端发送过来的数据
                */
                 InputStream in = socket.getInputStream();
                 InputStreamReader isr = new InputStreamReader(in, StandardCharsets.UTF_8);
                 BufferedReader br = new BufferedReader(isr);

                 //通过socket获取输入流用于向客户端发送数据
                 OutputStream out =socket.getOutputStream();
                 OutputStreamWriter osr=new OutputStreamWriter(out,StandardCharsets.UTF_8);
                 BufferedWriter bw =new BufferedWriter(osr);
                 PrintWriter pw =new PrintWriter(bw,true);//true 自动行刷新，一个pw对应一个客户端
                 allout.add(pw);//将客户端输出流存入共享集合中



                 String message;
                 while ((message= br.readLine())!=null){//读取客户端发生过来的消息
                   /*  System.out.println("客户端说:"+message);//服务端输出客户端发生过来的消息
                     pw.println("客户端说:"+message);//将消息发送回客户端 服务端读这里*/
                     sendMessageToAll("客户端说:"+message);//将消息发送给所有人
                 }

                /*
                  当我们通过br.readLine()读取对方发送过来的一行字符串时，
                  远端计算机如果调用socket.close()正常与我们断开连接(进行四次挥手)，
                  那么readLine()方法会返回null。
                  若对方是异常中断(强制杀死进程)，此时readLine()会抛出异常。
                  这种情况咱们服务器端是没有办法的
                 */
//                 String message;
//                 while ((message = br.readLine()) != null) { //读取客䚮端发送过来的消息
//                     System.out.println("客户端说:" + message);
//                 }

             } catch (IOException e) {
             }finally {
                 try {
                     socket.close();
                 } catch (IOException e) {

                 }
             }
         }

          //将消息发送给所有客户端(群聊)
         private  void sendMessageToAll(String message){
             System.out.println(message);
           for (PrintWriter o:allout){
               o.println(message);
           }

         }
    }
}